﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AAA.Meta.Quote.Data;
using AAA.Base.Util.SafeCollection;

namespace AAA.Finacial.Indicator
{
    public class TechIndex
    {
        public const string FAST_EMA_VALUE = "FastEmaValue";
        public const string SLOW_EMA_VALUE = "SlowEmaValue";
        public const string DIF_VALUE = "DifValue";
        public const string MACD_VALUE = "MacdValue";
        public const string RSV_VALUE = "RsvValue";
        public const string K_VALUE = "KValue";
        public const string D_VALUE = "DValue";
        public const string TP_VALUE = "TpValue";
        public const string TP_MA_VALUE = "TpMaValue";
        public const string TP_DIFF_VALUE = "TpDiffValue";
        public const string MD_VALUE = "MdValue";

        public const string CONTINUE_UP = "ContinueUp";
        public const string CONTINUE_DOWN = "ContinueDown";

        public const string DIFF_RATIO = "DiffRatio";

        public const int UP = 1;
        public const int DOWN = -1;

        public static ThreadSafeList<BarRecord> DiffRatio(ThreadSafeList<BarRecord> lstSource, string strNumerator, string strDenominator)
        {
            return DiffRatio(lstSource, strNumerator, strDenominator, DIFF_RATIO);
        }

        public static ThreadSafeList<BarRecord> DiffRatio(ThreadSafeList<BarRecord> lstSource, string strNumerator, string strDenominator, string strTargetField)
        {
            return DiffRatio(lstSource, strNumerator, strDenominator, strTargetField, 0);
        }

        public static ThreadSafeList<BarRecord> DiffRatio(ThreadSafeList<BarRecord> lstSource, string strNumerator, string strDenominator, string strTargetField, int iStartIndex)
        {
            return DiffRatio(lstSource, strNumerator, strDenominator, strTargetField, iStartIndex, 0);
        }

        public static ThreadSafeList<BarRecord> DiffRatio(ThreadSafeList<BarRecord> lstSource, string strNumerator, string strDenominator, string strTargetField, int iStartIndex, int iDenominatorOffset)
        {
            return DiffRatio(lstSource, strNumerator, strDenominator, strTargetField, iStartIndex, iDenominatorOffset, 1);
        }

        public static ThreadSafeList<BarRecord> DiffRatio(ThreadSafeList<BarRecord> lstSource, string strNumerator, string strDenominator, string strTargetField, int iStartIndex, int iDenominatorOffset, float fMultipler)
        {
            if(lstSource == null)
                return lstSource;

            if (lstSource.Count <= iStartIndex + iDenominatorOffset)
                return lstSource;

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                if (i - iDenominatorOffset < 0)
                {
                    lstSource[i][strTargetField] = float.NaN;
                    continue;
                }

                if (lstSource[i - iDenominatorOffset][strDenominator] == 0)
                {
                    lstSource[i][strTargetField] = float.NaN;
                    continue;
                }

                if (float.IsNaN(lstSource[i - iDenominatorOffset][strDenominator]))
                {
                    lstSource[i][strTargetField] = float.NaN;
                    continue;
                }

                lstSource[i][strTargetField] = (lstSource[i][strNumerator] - lstSource[i - iDenominatorOffset][strDenominator]) / (lstSource[i - iDenominatorOffset][strDenominator]) * fMultipler;
            }

            return lstSource;
        }


        public static ThreadSafeList<BarRecord> ContinueDirection(ThreadSafeList<BarRecord> lstSource, int iDirection)
        {
            return ContinueDirection(lstSource, BarRecord.CLOSE, iDirection);
        }

        public static ThreadSafeList<BarRecord> ContinueDirection(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iDirection)
        {
            return ContinueDirection(lstSource, strFieldName, iDirection, strFieldName + (iDirection == UP ? CONTINUE_UP : CONTINUE_DOWN));
        }

        public static ThreadSafeList<BarRecord> ContinueDirection(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iDirection, string strTargetName)
        {
            return ContinueDirection(lstSource, strFieldName, iDirection, 0, strTargetName);
        }

        public static ThreadSafeList<BarRecord> ContinueDirection(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iDirection, int iStartIndex, string strTargetName)
        {
            float fCurrentValue = float.NaN;
            int iContinueCount;

            if (lstSource == null)
                return lstSource;

            if (lstSource.Count <= iStartIndex)
                return lstSource;

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                fCurrentValue = lstSource[i][strFieldName];
                iContinueCount = 0;

                if (float.IsNaN(fCurrentValue))
                {
                    lstSource[i][strTargetName] = float.NaN;
                    continue;
                }

                for (int j = i - 1; j >= 0; j--)
                {
                    if (float.IsNaN(lstSource[j][strFieldName]))
                        break;
                    if (fCurrentValue * iDirection < lstSource[j][strFieldName] * iDirection)
                        break;
                    fCurrentValue = lstSource[j][strFieldName];
                    iContinueCount++;
                }

                lstSource[i][strTargetName] = iContinueCount;
            }

            return lstSource;
        }


        public static ThreadSafeList<BarRecord> MA(ThreadSafeList<BarRecord> lstSource, int iLen)
        {
            return MA(lstSource, BarRecord.CLOSE, iLen);
        }

        public static ThreadSafeList<BarRecord> MA(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iLen)
        {
            return MA(lstSource, strFieldName, iLen, strFieldName + "_MA_" + iLen);
        }

        public static ThreadSafeList<BarRecord> MA(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iLen, string strTargetName)
        {
            return MA(lstSource, strFieldName, iLen, 0, strTargetName);
        }

        public static ThreadSafeList<BarRecord> MA(ThreadSafeList<BarRecord> lstSource, string strFieldName, int iLen, int iStartIndex, string strTargetName)
        {
            float fSum = float.NaN;

            if (lstSource == null)
                return lstSource;

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                if (float.IsNaN(fSum))
                {
                    if (i < iLen - 1)
                    {
                        lstSource[i][strTargetName] = float.NaN;
                        continue;
                    }

                    fSum = 0;
                    for (int j = i - iLen + 1; j <= i; j++)
                    {
                        fSum += lstSource[j][strFieldName];
                    }
                }
                else
                {
                    fSum = fSum + lstSource[i][strFieldName] - lstSource[i - iLen][strFieldName];
                }

                lstSource[i][strTargetName] = (float)(fSum / iLen);
            }

            return lstSource;
        }

        public static ThreadSafeList<BarRecord> CCI(ThreadSafeList<BarRecord> lstSource, int iLen)
        {
            return CCI(lstSource, iLen, "CCI_" + iLen);
        }

        public static ThreadSafeList<BarRecord> CCI(ThreadSafeList<BarRecord> lstSource, int iLen, string strTargetName)
        {
            return CCI(lstSource, iLen, (float)0.015, strTargetName);
        }

        public static ThreadSafeList<BarRecord> CCI(ThreadSafeList<BarRecord> lstSource, int iLen, float fCCIRatio, string strTargetName)
        {
            return CCI(lstSource, iLen, fCCIRatio, 0, strTargetName);
        }

        public static ThreadSafeList<BarRecord> CCI(ThreadSafeList<BarRecord> lstSource, int iLen, float fCCIRatio, int iStartIndex, string strTargetName)
        {
            string strTPName = strTargetName + "_" + TP_VALUE;            
            string strTPMAName = strTargetName + "_" + TP_MA_VALUE;
            string strTPDiffName = strTargetName + "_" + TP_DIFF_VALUE;
            string strMDName = strTargetName + "_" + MD_VALUE;

            float fTP = float.NaN;
            float fTPMA = float.NaN;
            
            float fMD = float.NaN;

            if (lstSource == null)
                return lstSource;

            if (iStartIndex > 0)
                if (float.IsNaN(lstSource[iStartIndex - 1][strTPMAName]))
                    iStartIndex = 0;

            float fTPSum = float.NaN;
            float fTPDiffSum = float.NaN;

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                lstSource[i][strTPName] = (float)((lstSource[i][BarRecord.HIGH] + lstSource[i][BarRecord.LOW] + lstSource[i][BarRecord.CLOSE]) / 3.0);
                if (float.IsNaN(fTPSum))
                {
                    if (i < iLen - 1)
                    {
                        lstSource[i][strTPMAName] = float.NaN;
                        lstSource[i][strMDName] = float.NaN;
                        continue;
                    }
                    else
                    {
                        // 計算TPMA值
                        fTPSum = 0;
                        for (int j = i - iLen + 1; j <= i; j++)
                        {
                            fTPSum += lstSource[j][strTPName];
                        }
                    }
                }
                else
                {
                    fTPSum = fTPSum + lstSource[i][strTPName] - lstSource[i - iLen][strTPName];
                }

                lstSource[i][strTPMAName] = fTPSum / iLen;
                lstSource[i][strTPDiffName] = Math.Abs(lstSource[i][strTPName] - lstSource[i][strTPMAName]);
                lstSource = MA(lstSource, strTPDiffName, iLen, i - iLen + 1, strMDName);

                lstSource[i][strTargetName] = (lstSource[i][strTPName] - lstSource[i][strTPMAName]) / (fCCIRatio * lstSource[i][strMDName]);
            }

            return lstSource;
        }

        public static ThreadSafeList<BarRecord> KD(ThreadSafeList<BarRecord> lstSource, int iLen)
        {
            return KD(lstSource, iLen, "KD_" + iLen);
        }

        public static ThreadSafeList<BarRecord> KD(ThreadSafeList<BarRecord> lstSource, int iLen, string strTargetName)
        {
            return KD(lstSource, iLen, 0, strTargetName);
        }

        public static ThreadSafeList<BarRecord> KD(ThreadSafeList<BarRecord> lstSource, int iLen, int iStartIndex, string strTargetName)
        {
            string strKName = strTargetName + "_" + K_VALUE;
            string strDName = strTargetName + "_" + D_VALUE;
            string strRSVName = strTargetName + "_" + RSV_VALUE;

            float fRSV = float.NaN;
            float fK = float.NaN;
            float fD = float.NaN;
            float fHighest;
            float fLowest;

            if (lstSource == null)
                return lstSource;

            if (iStartIndex >= lstSource.Count)
            {
                iStartIndex = lstSource.Count - 5;
            }

            if ((iStartIndex > 0) && (iStartIndex < lstSource.Count))
            {
                if (float.IsNaN(lstSource[iStartIndex - 1][strKName]))
                    iStartIndex = 0;
            }
            else
            {
                iStartIndex = 0;
            }

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                if (i < iLen - 1)
                {
                    lstSource[i][strKName] = float.NaN;
                    lstSource[i][strDName] = float.NaN;
                    lstSource[i][strRSVName] = float.NaN;
                    continue;
                }
                else
                {
                    if(float.IsNaN(lstSource[i - 1][strKName]))
                    {
                        lstSource[i - 1][strKName] = (float)50;
                        lstSource[i - 1][strDName] = (float)50;
                    }
                }

                fK = lstSource[i - 1][strKName];
                fD = lstSource[i - 1][strDName];

                fHighest = float.MinValue;
                fLowest = float.MaxValue;

                for (int j = i - iLen + 1; j <= i; j++)
                {
                    fHighest = Math.Max(lstSource[j][BarRecord.HIGH], fHighest);
                    fLowest = Math.Min(lstSource[j][BarRecord.LOW], fLowest);
                }

                fRSV = (lstSource[i][BarRecord.CLOSE] - fLowest) / (fHighest - fLowest) * 100;
                lstSource[i][strRSVName] = fRSV;
                lstSource[i][strKName] = (fK * 2 + fRSV) / 3;
                lstSource[i][strDName] = (fD * 2 + lstSource[i][strKName]) / 3;
            }
            return lstSource;
        }

        public static ThreadSafeList<BarRecord> MACD(ThreadSafeList<BarRecord> lstSource, int iFastLen, int iSlowLen, int iDemLen)
        {
            return MACD(lstSource, iFastLen, iSlowLen, iDemLen, "MACD_" + iFastLen + "_" + iSlowLen);
        }

        public static ThreadSafeList<BarRecord> MACD(ThreadSafeList<BarRecord> lstSource, int iFastLen, int iSlowLen, int iDemLen, string strTargetName)
        {
            return MACD(lstSource, iFastLen, iSlowLen, iDemLen, 0, strTargetName);
        }
        public static ThreadSafeList<BarRecord> MACD(ThreadSafeList<BarRecord> lstSource, int iFastLen, int iSlowLen, int iDemLen, int iStartIndex, string strTargetName)
        {
            string strFastName = strTargetName + "_" + FAST_EMA_VALUE;
            string strSlowName = strTargetName + "_" + SLOW_EMA_VALUE;
            string strDifName = strTargetName + "_" + DIF_VALUE;
            string strMacdName = strTargetName + "_" + MACD_VALUE;
            float fSum = float.NaN;
            float fFastEma = float.NaN;
            float fSlowEma = float.NaN;
            float fDif = float.NaN;
            float fMacd = float.NaN;

            if (lstSource == null)
                return lstSource;

            if (iStartIndex >= lstSource.Count)
            {
                iStartIndex = lstSource.Count - 5;
            }

            if ((iStartIndex > 0) && (iStartIndex < lstSource.Count))
            {
                if (float.IsNaN(lstSource[iStartIndex - 1][strFastName]))
                    iStartIndex = 0;
            }
            else
            {
                iStartIndex = 0;
            }

            for (int i = iStartIndex; i < lstSource.Count; i++)
            {
                if (i < iFastLen - 1)
                {
                    lstSource[i][strFastName] = float.NaN;
                }
                else
                {
                    if (float.IsNaN(lstSource[i - 1][strFastName]))
                    {
                        fSum = 0;
                        for (int j = i - iFastLen + 1; j <= i; j++)
                            fSum += lstSource[j][BarRecord.CLOSE];
                        lstSource[i][strFastName] = (float)(fSum / iFastLen);
                    }
                    else
                        lstSource[i][strFastName] = (lstSource[i - 1][strFastName] * (iFastLen - 1) + lstSource[i][BarRecord.CLOSE] * 2) / (iFastLen + 1);
                }

                if (i < iSlowLen - 1)
                {
                    lstSource[i][strSlowName] = float.NaN;
                }
                else
                {
                    if (float.IsNaN(lstSource[i - 1][strSlowName]))
                    {
                        fSum = 0;
                        for (int j = i - iSlowLen + 1; j <= i; j++)
                            fSum += lstSource[j][BarRecord.CLOSE];
                        lstSource[i][strSlowName] = (float)(fSum / iSlowLen);
                    }
                    else
                        lstSource[i][strSlowName] = (lstSource[i - 1][strSlowName] * (iSlowLen - 1) + lstSource[i][BarRecord.CLOSE] * 2) / (iSlowLen + 1);
                }

                if (float.IsNaN(lstSource[i][strFastName]) || float.IsNaN(lstSource[i][strSlowName]) || (i < iDemLen - 1))
                {
                    lstSource[i][strDifName] = float.NaN;
                    lstSource[i][strMacdName] = float.NaN;
                    continue;
                }

                lstSource[i][strDifName] = lstSource[i][strFastName] - lstSource[i][strSlowName];

                if (float.IsNaN(lstSource[i - iDemLen + 1][strDifName]))
                {
                    lstSource[i][strMacdName] = float.NaN;
                    continue;
                }

                if (float.IsNaN(lstSource[i - 1][strMacdName]))
                {
                    fSum = 0;
                    for (int j = i - iDemLen + 1; j <= i; j++)
                        fSum += lstSource[j][strDifName];
                    lstSource[i][strMacdName] = (float)(fSum / iDemLen);
                }
                else
                {
                    lstSource[i][strMacdName] = (lstSource[i - 1][strMacdName] * (iDemLen - 1) + lstSource[i][strDifName] * 2) / (iDemLen + 1);
                }
            }

            return lstSource;
        }
    }
}
